package com.csaba.swing.gui.util;

import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.logging.Logger;

import javax.swing.JTable;
import javax.swing.table.TableColumnModel;

import com.csaba.util.LoggerHelper;

/**
 * @author Gabe
 *This class is a singleton, that is responsible to collect and restore table column widths. 
 *Use the monitorTable() method to register a JTable object. If the registry already 
 *contains values for this table, and than it sets the column width automatically to the 
 *last known values, otherwise it saves the current values.
 *<p> The stored values are saved and restored to/from the config file. In this way the 
 *column widths are stored persistently. 
 *<p> It is recommended to register a table to the size container only once it is field up 
 *with values, and it is already set visible on the screen. Otherwise the JTable.doLayout() 
 *method overrides the restored column widths with the defaults.
 */
public class TableSizeContainer
{
	private final static Logger logger = LoggerHelper.getDefaultLogger();

	private static TableSizeContainer instance = new TableSizeContainer();

	public final static TableSizeContainer getInstance()
	{
		return instance;
	}

	/**
	 * This map stores column widths in int array format, or the reference to the actual table column model.
	 */
	private final Map<String, Object> columns = new HashMap<String, Object>();

	private TableSizeContainer()
	{
		// do not instantiate. this is a singleton
	}

	/**
	 * Parse column widt from config text, if can. If fails, then no table column widths will be
	 * set.  
	 * @param columnWidths String should look like "columnName1[2,3,4,5];columnName2[6,7,8,9]"
	 */
	public void parseString( final String columnWidths )
	{
		if ( columnWidths == null || columnWidths.length() < 4 )
		{
			logger.finer("No table column width definition in config: " + columnWidths);
			return; // not a valid setting
		}
		final StringTokenizer tables = new StringTokenizer(columnWidths, ";");
		tableLoop: while ( tables.hasMoreTokens() )
		{
			final String table = tables.nextToken();
			final int open = table.indexOf('[');
			final int close = table.indexOf(']');
			if ( open < 1 || close < open + 1 )
			{
				logger.finer("Invalid table column width definition in config: " + table);
				continue; // skip this invalid table definition 
			}

			final String tableName = table.substring(0, open);

			final StringTokenizer widths = new StringTokenizer(table.substring(open + 1, close), ",");
			if ( widths.countTokens() < 1 )
			{
				logger.finer("There is no column in table definition in config: " + table);
				continue; // skip this invalid table definition 
			}
			final int[] widthsArray = new int[widths.countTokens()];
			for ( int i = 0; widths.hasMoreElements(); i++ )
			{
				final String widthString = (String) widths.nextElement();
				try
				{
					widthsArray[i] = Integer.parseInt(widthString);
				}
				catch ( final Exception ex )
				{
					logger.finer("Invalid column width at position " + i + " in table definition in config: " + table);
					continue tableLoop;
				}
			}

			columns.put(tableName, widthsArray);
		}
	}

	@Override
	public String toString()
	{
		final StringBuilder builder = new StringBuilder();
		for ( final String key : columns.keySet() )
		{
			if ( builder.length() > 0 )
				builder.append(";");
			builder.append(key).append("[");
			final Object something = columns.get(key);
			;
			if ( something instanceof int[] )
			{
				final int[] widths = (int[]) something;
				for ( int i = 0; i < widths.length; i++ )
				{
					if ( i > 0 )
						builder.append(",");
					builder.append(widths[i]);
				}
			}
			else
			{
				final TableColumnModel model = (TableColumnModel) something;
				final int count = model.getColumnCount();
				for ( int i = 0; i < count; i++ )
				{
					if ( i > 0 )
						builder.append(",");
					builder.append(model.getColumn(i).getWidth());
				}
			}
			builder.append("]");
		}
		return builder.toString();
	}

	public void monitorTable( final String id, final JTable table )
	{
		final TableColumnModel model = table.getColumnModel();

		if ( columns.containsKey(id) )
		{
			logger.finest("Table registered, and the old column widths are being set: " + id);
			//if the table is already known, copy column widths to the newest model
			final int count = model.getColumnCount();

			final Object something = columns.get(id);
			if ( something instanceof int[] )
			{
				final int[] widths = (int[]) something;
				for ( int i = 0; i < widths.length; i++ )
				{
					if ( i < count )
						model.getColumn(i).setPreferredWidth(widths[i]);
					else
						break;
				}
			}
			else
			{
				final TableColumnModel other = (TableColumnModel) something;
				final int otherCount = other.getColumnCount();
				for ( int i = 0; i < count && i < otherCount; i++ )
				{
					model.getColumn(i).setPreferredWidth(other.getColumn(i).getWidth());
				}
			}
		}
		else
			logger.finest("Table newly registered: " + id);

		columns.put(id, model);
	}

}
